#include "Window.h"

#include <iostream>
#include <SDL_opengl.h>

#include "Renderer.h"
#include "Vector2i.h"

PF::Window::Window()
	:	m_pWindow(NULL), m_pContext(NULL),
		m_pRenderer(NULL), m_pSkin(NULL), m_pCanvas(NULL)
{
}

PF::Window::~Window()
{
	m_pCanvas->Release();
	delete m_pSkin;

	delete m_pRenderer;

	SDL_GL_DeleteContext(m_pContext);
	SDL_DestroyWindow(m_pWindow);
}

bool PF::Window::Create(const char *a_szTitle, int a_iX, int a_iY, int a_iW, int a_iH, Uint32 a_uiFlags)
{
	m_pWindow = SDL_CreateWindow(a_szTitle, a_iX, a_iY, a_iW, a_iH, a_uiFlags);
	if(m_pWindow == NULL)
	{
		std::cout << "Could not create window: " << SDL_GetError() << ".\n";
		return 0;
	}

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

	m_pContext = SDL_GL_CreateContext(m_pWindow);
	if(m_pContext == NULL)
	{
		std::cout << "Could not create context: " << SDL_GetError() << ".\n";
		return 0;
	}

	glViewport(0, 0, a_iW, a_iH);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0.0f, a_iW, a_iH, 0.0f, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	SDL_GL_SetSwapInterval(1);

	m_pRenderer = new PF::Renderer();

	m_pSkin = new Gwen::Skin::TexturedBase();
	m_pSkin->SetRender(m_pRenderer);
	m_pSkin->Init("images/DefaultSkin.png");
	m_pSkin->SetDefaultFont(L"fonts/OpenSans.ttf", 10.0f);
	m_pRenderer->LoadFont(m_pSkin->GetDefaultFont());

	m_pCanvas = new Gwen::Controls::Canvas(m_pSkin);
	m_pCanvas->SetSize(a_iW, a_iH);
	m_pCanvas->SetDrawBackground(true);
	return 1;
}

void PF::Window::Clear(Uint8 a_uiR, Uint8 a_uiG, Uint8 a_uiB, Uint8 a_uiA)
{
	m_pCanvas->SetBackgroundColor(Gwen::Color(a_uiR, a_uiG, a_uiB, a_uiA));
}

void PF::Window::Update(float a_fDeltaTime)
{
	SDL_Event kEvent;
	while(SDL_PollEvent(&kEvent))
	{
		OnEvent(&kEvent);
	}
}

void PF::Window::Render()
{
	SDL_GL_MakeCurrent(m_pWindow, m_pContext);

	m_pCanvas->RenderCanvas();
	SDL_GL_SwapWindow(m_pWindow);
}

void PF::Window::OnEvent(SDL_Event *a_pEvent)
{
    switch(a_pEvent->type)
	{
        case SDL_KEYUP:
        case SDL_KEYDOWN:
		{
            OnKeyboardEvent(&a_pEvent->key);
            break;
        }
        case SDL_MOUSEMOTION:
		{
            OnMouseMotionEvent(&a_pEvent->motion);
            break;
        }
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
		{
            OnMouseButtonEvent(&a_pEvent->button);
            break;
        }
        case SDL_MOUSEWHEEL:
		{
            OnMouseWheelEvent(&a_pEvent->wheel);
            break;
        }
        case SDL_TEXTINPUT:
		{
            OnTextInputEvent(&a_pEvent->text);
			break;
        }
		case SDL_QUIT:
		{
			break;
		}
		default:
		{
			break;
		}
    }
}

void PF::Window::OnKeyboardEvent(SDL_KeyboardEvent* a_pEvent)
{ 
    unsigned char ucGwenKey = 0;
    switch (a_pEvent->keysym.scancode) {
        case SDL_SCANCODE_RETURN: ucGwenKey = Gwen::Key::Return; break;
        case SDL_SCANCODE_BACKSPACE: ucGwenKey = Gwen::Key::Backspace; break;
        case SDL_SCANCODE_DELETE: ucGwenKey = Gwen::Key::Delete; break;
        case SDL_SCANCODE_LEFT: ucGwenKey = Gwen::Key::Left; break;
        case SDL_SCANCODE_RIGHT: ucGwenKey = Gwen::Key::Right; break;
        case SDL_SCANCODE_LSHIFT: ucGwenKey = Gwen::Key::Shift; break;
        case SDL_SCANCODE_RSHIFT: ucGwenKey = Gwen::Key::Shift; break;
        case SDL_SCANCODE_TAB: ucGwenKey = Gwen::Key::Tab; break;
        case SDL_SCANCODE_SPACE: ucGwenKey = Gwen::Key::Space; break;
        case SDL_SCANCODE_HOME: ucGwenKey = Gwen::Key::Home; break;
        case SDL_SCANCODE_END: ucGwenKey = Gwen::Key::End; break;
        case SDL_SCANCODE_LCTRL: ucGwenKey = Gwen::Key::Control; break;
        case SDL_SCANCODE_RCTRL: ucGwenKey = Gwen::Key::Control; break;
        case SDL_SCANCODE_UP: ucGwenKey = Gwen::Key::Up; break;
        case SDL_SCANCODE_DOWN: ucGwenKey = Gwen::Key::Down; break;
        case SDL_SCANCODE_ESCAPE: ucGwenKey = Gwen::Key::Escape; break;
        case SDL_SCANCODE_LALT: ucGwenKey = Gwen::Key::Alt; break;
        case SDL_SCANCODE_RALT: ucGwenKey = Gwen::Key::Alt; break;
		default: break;
    }

    m_pCanvas->InputKey(ucGwenKey, (a_pEvent->state == 1));
    return;
}

void PF::Window::OnMouseMotionEvent(SDL_MouseMotionEvent* a_pEvent)
{
    m_pCanvas->InputMouseMoved(a_pEvent->x, a_pEvent->y, a_pEvent->xrel, a_pEvent->yrel);
}
 
void PF::Window::OnMouseButtonEvent(SDL_MouseButtonEvent* a_pEvent)
{
    Uint8 uiButton = -1;
    switch (a_pEvent->button)
	{
        case 1: uiButton = 0; break;
        case 3: uiButton = 1; break;
        default: break;
    }

    m_pCanvas->InputMouseButton(uiButton, (a_pEvent->state == 1));
}
 
void PF::Window::OnMouseWheelEvent(SDL_MouseWheelEvent* a_pEvent)
{
    m_pCanvas->InputMouseWheel(a_pEvent->y);
}

void PF::Window::OnTextInputEvent(SDL_TextInputEvent* a_pEvent)
{
    Gwen::UnicodeString szText = Gwen::Utility::StringToUnicode(a_pEvent->text);
    for (unsigned int i = 0; i < szText.length(); ++i)
	{
        m_pCanvas->InputCharacter(szText[i]);
    }
}

SDL_Window *PF::Window::GetHandle()
{
	return m_pWindow;
}

SDL_GLContext PF::Window::GetContext()
{
	return m_pContext;
}

Gwen::Renderer::Base *PF::Window::GetRenderer()
{
	return m_pRenderer;
}

void PF::Window::SetSkin(const Gwen::TextObject &a_kTextureName)
{
	m_pSkin->Init(a_kTextureName);
}

Gwen::Controls::Canvas *PF::Window::GetCanvas()
{
	return m_pCanvas;
}

Uint32 PF::Window::GetFlags()
{
	return SDL_GetWindowFlags(m_pWindow);
}

PF::Vector2i PF::Window::GetPosition()
{
	PF::Vector2i vPosition;
	SDL_GetWindowPosition(m_pWindow, &vPosition.x, &vPosition.y);

	return vPosition;
}

void PF::Window::SetPosition(int a_iX, int a_iY)
{
	SDL_SetWindowPosition(m_pWindow, a_iX, a_iY);
}

PF::Vector2i PF::Window::GetSize()
{
	PF::Vector2i vSize;
	SDL_GetWindowSize(m_pWindow, &vSize.x, &vSize.y);

	return vSize;
}

void PF::Window::SetSize(int a_iW, int a_iH)
{
	SDL_SetWindowSize(m_pWindow, a_iW, a_iH);

	glViewport(0, 0, a_iW, a_iH);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0.0f, a_iW, a_iH, 0.0f, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	m_pCanvas->SetSize(a_iW, a_iH);
}

const char *PF::Window::GetTitle()
{
	return SDL_GetWindowTitle(m_pWindow);
}

void PF::Window::SetTitle(const char *a_szTitle)
{
	SDL_SetWindowTitle(m_pWindow, a_szTitle);
}

bool PF::Window::GetFullscreen()
{
	return m_bFullscreen;
}

void PF::Window::SetFullscreen(bool a_bFullscreen)
{
	m_bFullscreen = a_bFullscreen;
	SDL_SetWindowFullscreen(m_pWindow, (SDL_bool) a_bFullscreen);
}
